from util import *

class Alphabeta_Player():
    def __init__(self, turn, maxdepth = 6):
        self.BOARD_SIZE = 8
        self.PLAYER_NUM = 'none'
        self.COMPUTER_NUM = 'none'
        self.direction = [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]]
        if turn == 'player':
            self.PLAYER_NUM = 'black'
            self.COMPUTER_NUM = 'white'
        else:
            self.PLAYER_NUM = 'white'
            self.COMPUTER_NUM = 'black'
        self.empty = 'none'
        self.color = self.COMPUTER_NUM
        self.maxdeepth = maxdepth

    weight = [
        [70, -20, 20, 20, 20, 20, -15, 70], 
	    [-20,-30,5,5,5,5,-30,-15], 
	    [20,5,1,1,1,1,5,20],
	    [20,5,1,1,1,1,5,20],
	    [20,5,1,1,1,1,5,20],
        [20,5,1,1,1,1,5,20],
	    [-20,-30,5,5,5,5,-30,-15],
	    [70,-15,20,20,20,20,-15,70]
    ]

    deepth = 0
    emptylistFlag = 10000000


    def getBoardCopy(self, board):
        dupeBoard = self.getInitialBoard()
    
        for x in range(8):
            for y in range(8):
                dupeBoard[x][y] = board[x][y]
    
        return dupeBoard


    def getInitialBoard(self):
        board = []

        for i in range(0, self.BOARD_SIZE):
            board.append([])

            for j in range(0, self.BOARD_SIZE):
                board[i].append('none')

        board[self.BOARD_SIZE // 2 - 1][self.BOARD_SIZE // 2 - 1] = self.COMPUTER_NUM
        board[self.BOARD_SIZE // 2][self.BOARD_SIZE // 2] = self.COMPUTER_NUM

        board[self.BOARD_SIZE // 2 - 1][self.BOARD_SIZE // 2] = self.PLAYER_NUM
        board[self.BOARD_SIZE // 2][self.BOARD_SIZE // 2 - 1] = self.PLAYER_NUM

        return board

    def calculate(self,board,color):
        if color == 'black':
            colorNext ='white'
        else:
            colorNext ='black'
        count = 0
        for i in range(8):
            for j in range(8):
                if color == board[i][j]: 
                    count += self.weight[i][j]
                elif colorNext == board[i][j]:
                    count -= self.weight[i][j]
        return count

    def alphaBeta(self,board,color,a,b):
        def backpropagation(board, action, flipped_pos, color):
            """
            回溯
            :param action: 落子点的坐标
            :param flipped_pos: 反转棋子坐标列表
            :param color: 棋子的属性，[X,0,.]三种情况
            :return:
            """

            board[action[0]][action[1]] = self.empty
            # 如果 color == 'X'，则 op_color = 'O';否则 op_color = 'X'
            op_color = 'white' if color == 'black' else 'black'

            for p in flipped_pos:
                board[p[0]][p[1]] = op_color


        def _move(board, action, color):
            """
            落子并获取反转棋子的坐标
            :param action: 落子的坐标 可以是 D3 也可以是(2,3)
            :param color: 表示棋盘上不同的棋子
            :return: 返回反转棋子的坐标列表，落子失败则返回False
            """
            # 判断action 是不是字符串，如果是则转化为数字坐标

            fliped = _can_fliped(board, action, color)

            if fliped:
                # 有就反转对方棋子坐标
                for flip in fliped:
                    x, y = flip[0], flip[1]
                    board[x][y] = color

                # 落子坐标
                x, y = action
                # 更改棋盘上 action 坐标处的状态，修改之后该位置属于 color[X,O,.]等三状态
                board[x][y] = color
                return fliped
            else:
                # 没有反转子则落子失败
                return False


        def _can_fliped(board, action, color):
            """
            检测落子是否合法,如果不合法，返回 False，否则返回反转子的坐标列表
            :param action: 下子位置
            :param color: [X,0,.] 棋子状态
            :return: False or 反转对方棋子的坐标列表
            """
            # 判断action 是不是字符串，如果是则转化为数字坐标
            xstart, ystart = action

            # 如果该位置已经有棋子或者出界，返回 False
            if not isOnBoard(xstart, ystart) or board[xstart][ystart] != self.empty:
                return False

            # 临时将color放到指定位置
            board[xstart][ystart] = color
            # 棋手
            op_color = 'white' if color == 'black' else 'black'

            # 要被翻转的棋子
            flipped_pos = []
            flipped_pos_board = []

            for xdirection, ydirection in [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0],
                                        [-1, 1]]:
                x, y = xstart, ystart
                x += xdirection
                y += ydirection
                # 如果(x,y)在棋盘上，而且为对方棋子,则在这个方向上继续前进，否则循环下一个角度。
                if isOnBoard(x, y) and board[x][y] == op_color:
                    x += xdirection
                    y += ydirection
                    # 进一步判断点(x,y)是否在棋盘上，如果不在棋盘上，继续循环下一个角度,如果在棋盘上，则进行while循环。
                    if not isOnBoard(x, y):
                        continue
                    # 一直走到出界或不是对方棋子的位置
                    while board[x][y] == op_color:
                        # 如果一直是对方的棋子，则点（x,y）一直循环，直至点（x,y)出界或者不是对方的棋子。
                        x += xdirection
                        y += ydirection
                        # 点(x,y)出界了和不是对方棋子
                        if not isOnBoard(x, y):
                            break
                    # 出界了，则没有棋子要翻转OXXXXX
                    if not isOnBoard(x, y):
                        continue

                    # 是自己的棋子OXXXXXXO
                    if board[x][y] == color:
                        while True:
                            x -= xdirection
                            y -= ydirection
                            # 回到了起点则结束
                            if x == xstart and y == ystart:
                                break
                            # 需要翻转的棋子
                            flipped_pos.append([x, y])

            # 将前面临时放上的棋子去掉，即还原棋盘
            board[xstart][ystart] = self.empty  # restore the empty space

            # 没有要被翻转的棋子，则走法非法。返回 False
            if len(flipped_pos) == 0:
                return False

            for fp in flipped_pos:
                flipped_pos_board.append(fp)
            # 走法正常，返回翻转棋子的棋盘坐标
            return flipped_pos_board


        # 递归终止
        if self.deepth > self.maxdeepth:
                return None, self.calculate(board,self.color)

        if color == 'black':
            colorNext ='white'
        else:
            colorNext ='black'
        action_list = getValidMoves(board, color)
        if len(action_list) == 0:
            if len(getValidMoves(board, colorNext)) == 0:
                return None,self.calculate(board,self.color)
            return self.alphaBeta(board,colorNext,a,b)
        
        max = -9999999
        min = 9999999
        action = None

        for p in action_list:

            flipped_pos = _move(board,p,color)
            self.deepth += 1
            p1, current = self.alphaBeta(board,colorNext,a,b)
            self.deepth -= 1
            backpropagation(board, p,flipped_pos,color)
            # print(p,current)
            # alpha-beta 剪枝
            if color == self.color:
                if current > a:
                    if current > b:
                        return p,current
                    a = current
                if current > max:
                    max = current
                    action = p

            else:
                if current < b:
                    if current < a:
                        return p,current
                    b = current
                if current < min:
                    min = current
                    action = p

        # print(color,action,max,min)

        if color == self.color:
            return action,max
        else:
            return action,min         


    def NextPosition(self, board):
        """
        根据当前棋盘状态获取最佳落子位置
        :param board: 棋盘
        :return: action 最佳落子位置, e.g. 'A1'
        """
        bourdcopy = self.getBoardCopy(board)

        # -----------------请实现你的算法代码--------------------------------------
        action_list = getValidMoves(bourdcopy, self.color)
        
        # action, weight = self.maxMin(board,self.color)
        action, weight = self.alphaBeta(bourdcopy,self.color,-9999999,9999999)

        if len(action_list) == 0:
            return None
        print(action_list)
        print(action)
        return action
        # ------------------------------------------------------------------------
